package com.yuntsg.nnsfcp.service.front.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.yuntsg.nnsfcp.common.utils.CheckUtil;
import com.yuntsg.nnsfcp.front.entity.UserInfo;
import com.yuntsg.nnsfcp.front.mapper.UserInfoMapper;
import com.yuntsg.nnsfcp.common.utils.RedisUtil;
import com.yuntsg.nnsfcp.util.httpclient.SmallTool;
import org.apache.commons.lang.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.context.ApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;

import javax.servlet.ServletContext;
import javax.servlet.http.HttpSession;
import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

/**
 * session 监听器
 * 监听session的销毁, session销毁时顺便销毁此用户的在线状态
 *
 * @author ：ljx
 * @date ：Created in 2019/8/11
 */
public class SessionListener implements HttpSessionListener {
    /**
     * 存储在线的用户信息
     */
    private static Map<String, Integer> userLoginSessionMap = new HashMap<>();
    private static Logger log = LogManager.getLogger(SessionListener.class);

    /**
     * 清空map
     */
    public static void clearMap() {
        userLoginSessionMap = new HashMap<>();
    }

    /**
     * 判断用户是否在线
     * 用于判断用户是否已经登录以及相应的处理方法
     *
     * @param session httpSession
     * @return
     */
    public static boolean isLogin(HttpSession session) {
        boolean flag = true;
        if (!userLoginSessionMap.containsKey(session.getId())) {
            flag = false;
            log.info("未登录");
        }
        return flag;
    }

    /**
     * 判断用户是否在线
     * 用于判断用户是否已经登录以及相应的处理方法
     *
     * @param userId 用户id
     * @return
     */
    public static boolean isLogin(Integer userId) {
        boolean flag = true;
        if (!userLoginSessionMap.containsValue(userId)) {
            flag = false;
            log.info("未登录");
        }
        return flag;
    }

    /**
     * 存储用户的登陆状态
     *
     * @param session httpsession
     */
    public static void addUserLogin(HttpSession session) {

        SmallTool.printMess("添加用户登陆状态: " + session.getId());

        Object userIdObj = SessionListener.getSessionByUserInfo(session, "frontUserId");
        CheckUtil.notNull(userIdObj, "请登录");

        try {
            Integer uid = Integer.valueOf(userIdObj + "");
            if (userLoginSessionMap.containsValue(uid)) {
                Object key = null;
                Object val = null;
                Iterator iter = userLoginSessionMap.entrySet().iterator();
                while (iter.hasNext()) {
                    Map.Entry entry = (Map.Entry) iter.next();
                    key = entry.getKey();
                    val = entry.getValue();
                }
                CheckUtil.notNull(key, "添加用户在线失败了CODE:" + key);
                CheckUtil.notNull(val, "添加用户在线失败了CODE:" + val);
                //删除原来的sessionId
                if (val.equals(uid)) {
                    log.info("重新添加用户的在线状态");
                    userLoginSessionMap.remove(key);
                }
            }
            //添加SessionId
            userLoginSessionMap.put(session.getId(), uid);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 用户退出登陆
     *
     * @param session session
     */
    public static void removeLogin(HttpSession session, UserInfoMapper userInfoMapper) {
        String id = session.getId();

        Integer userId = userLoginSessionMap.get(id);
        if (userId != null) {
            UserInfo userInfo = userInfoMapper.selectOne(new QueryWrapper<UserInfo>()
                    .eq("id", userId).eq("is_delete", 0));
            if (userInfo != null) {
                userInfo.setToken("");
                userInfo.setLastTime(LocalDateTime.now());
                //userInfo.setIsDelete(null);
                userInfoMapper.updateById(userInfo);
                log.info("退出:" + userInfo.getName());
            }
            userLoginSessionMap.remove(id);
        } else {
            log.info("退出未获取到用户id: " + userId);
        }
    }

    /**
     * 打印在线用户列表
     */
    public static void getLoginUser() {
        SmallTool.printMess("=============================在线用户列表================================");
        SmallTool.printMess(userLoginSessionMap.toString());
        SmallTool.printMess("=============================在线用户列表================================");
    }

    /**
     * 将用户的一些信息都set到session中 避免前台数据被篡改
     *
     * @param userInfo 用户信息.
     */
    public static void setSessionByUserInfo(HttpSession session, UserInfo userInfo) {
        session.setAttribute("frontUserId", userInfo.getId());
        session.setAttribute("frontUserName", userInfo.getName());
        session.setAttribute("frontUserToken", userInfo.getToken());
        session.setAttribute("frontUserPhone", userInfo.getPhone());
        session.setAttribute("frontUserAccount", userInfo.getAccount());
//        log.info("存储的信息+++" + userInfo);
    }

    /**
     * session 中获取用户信息
     *
     * @param session    servletSession
     * @param sessionKey session key
     * @return
     */
    public static Object getSessionByUserInfo(HttpSession session, String sessionKey) {
        Object attribute = session.getAttribute(sessionKey);
        if (attribute == null) {
            return null;
        }
        return attribute;
    }

    /**
     * 通过WebApplicationContextUtils 得到Spring容器的实例。根据bean的名称返回bean的实例。
     * 注意获取bean 的名字格式例如: 类名UserInfo, 获取userInfo ,首字母小写
     *
     * @param servletContext ：ServletContext上下文。
     * @param beanName       :要取得的Spring容器中Bean的名称。
     * @return 返回Bean的实例。
     */
    private Object getObjectFromApplication(ServletContext servletContext, String beanName) {
        //注意获取bean 的名字格式例如: 类名UserInfo, 获取userInfo ,首字母小写
        //通过WebApplicationContextUtils 得到Spring容器的实例。
        ApplicationContext application = WebApplicationContextUtils.getWebApplicationContext(servletContext);
        //返回Bean的实例。
        return application.getBean(beanName);
    }

    /**
     * session会话过期自动销毁, 同时销毁存储的在线用户
     *
     * @param se
     */
    @Override
    public void sessionDestroyed(HttpSessionEvent se) {
        SmallTool.printMess("********session销毁*********");
        SmallTool.printMess("销毁id: " + se.getSession().getId());
        String userPhone = null;
        String userName = null;

        try {
            Object uidObj = userLoginSessionMap.get(se.getSession().getId());
            if (uidObj != null) {

                //打印在线用户列表
                getLoginUser();

                SmallTool.printMess("移除在线用户ID: " + uidObj);
                Integer uid = Integer.valueOf(uidObj + "");
                UserInfoMapper userInfoMapper = (UserInfoMapper) getObjectFromApplication(se.getSession().getServletContext(), "userInfoMapper");

                UserInfo userInfo = userInfoMapper.selectOne(new QueryWrapper<UserInfo>().eq("id", uid).eq("is_delete", 0));
                if (userInfo != null) {
                    userPhone = userInfo.getPhone();
                    userName = userInfo.getName();
                    //记录一下退出的时间
                    userInfo.setLastTime(LocalDateTime.now());
                    //清空token
                    userInfo.setToken("");
                    //userInfo.setIsDelete(null);
                    userInfoMapper.updateById(userInfo);
                } else {
                    log.error("session销毁: 未知用户ID:" + uid);
                }
                //删除在线状态
                userLoginSessionMap.remove(se.getSession().getId());
                log.info("session销毁用户退出: " + userName);
            }
        } catch (Exception e) {
            log.error("context上下文获取 Bean 失败了");
            e.printStackTrace();
        }
        if (StringUtils.isNotEmpty(userPhone)) {
            //删除存储的woscookie
            RedisUtil.delString(userPhone);
            userPhone = null;
            userName = null;
        }
    }
}
